﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Mime;

namespace NHINDirect
{
    public static class Protocol
    {
        public const char CR = '\r';
        public const char LF = '\n';
        public const string CRLF = "\r\n";
        public const char Escape = '\\';
        public const char NameValueSeparator = ':';
        public const char BoundaryChar = '-';
        public const string BoundarySeparator = "--";
        //
        // Common Headers
        //
        public const string ToHeader = "To";
        public const string FromHeader = "From";
        public const string CCHeader = "CC";
        public const string MessageIDHeader = "Message-ID";
        public const string SubjectHeader = "Subject";
        public const string DateHeader = "Date";
        
        public const string MimeHeaderPrefix = "Content-";
        public const string MimeVersionHeader = "MIME-Version";
        public const string ContentTypeHeader = "Content-Type";
        public const string ContentIDHeader = "Content-ID";
        public const string ContentDispositionHeader = "Content-Disposition";
        public const string ContentDescriptionHeader = "Content-Description";
        public const string ContentTransferEncodingHeader = "Content-Transfer-Encoding";

        public const string TransferEncodingBase64 = "base64";
        public const string TransferEncoding7Bit = "7bit";
        public const string TransferEncodingQuoted = "quoted-printable";
        //
        // MIME
        //
        public const string MediaType_Multipart = "multipart";
        public const string MultiPartType_Mixed = "multipart/mixed;";
        public const string MultiPartType_Signed = "multipart/signed; protocol=\"application/pkcs7-signature\";";
        //
        // Cryptography
        //
        public const string EncryptedContentTypeHeaderValue = "application/pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\"";
        public const string EncryptedContentMediaType = "application/pkcs7-mime";
        public const string EncryptedContentMediaTypeAlternative = "application/x-pkcs7-mime";   // we are forgiving when we receive messages
        public const string SignatureContentTypeHeaderValue = "application/pkcs7-signature; name=\"smime.p7s\"";
        public const string SignatureContentMediaType = "application/pkcs7-signature";
        public const string SignatureContentMediaTypeAlternative = "application/x-pkcs7-signature"; // we are forgiving when we receive messages
        public const string SignatureDisposition = "attachment; filename=\"smime.p7s\"";
        
        public const string SmimeTypeParameterKey = "smime-type";
        public const string EnvelopedDataSmimeType = "enveloped-data";
        public const string DefaultFileName = "smime.p7m";
        //
        // MAIL
        //
        public const char MailAddressSeparator = ',';
        //
        // Other..
        //
        public static bool IsWhitespace(char ch)
        {
            //
            // CR/LF are reserved characters
            //
            return (ch == ' ' || ch == '\t');
        }
        
        public static bool IsEncryptedContent(ContentType contentType)
        {
            if (contentType == null)
            {
                throw new ArgumentNullException();
            }
            
            return (Protocol.Equals(contentType.MediaType, Protocol.EncryptedContentMediaType)
                   || Protocol.Equals(contentType.MediaType, Protocol.EncryptedContentMediaTypeAlternative));
        }

        public static bool IsSignedContent(ContentType contentType)
        {
            if (contentType == null)
            {
                throw new ArgumentNullException();
            }

            return (Protocol.Equals(contentType.MediaType, Protocol.SignatureContentMediaType)
                   || Protocol.Equals(contentType.MediaType, Protocol.SignatureContentMediaTypeAlternative));
        }

        internal static string Combine(string name, string value)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("name");
            }
            if (string.IsNullOrEmpty(value))
            {
                throw new ArgumentException("value");
            }
            
            StringBuilder builder = new StringBuilder();
            builder.Append(name);
            builder.Append(Protocol.NameValueSeparator);
            builder.Append(value);
            return builder.ToString();
        }
        
        //
        // RFC: 
        //  - All string comparisions are case-insensitive        
        //  - locale independant - i.e. ordinal
        //
        internal static bool Equals(string x, string y)
        {
            return string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
        }
        
        internal static bool StartsWith(string x, string y)
        {
            return x.StartsWith(y, StringComparison.OrdinalIgnoreCase);
        }

        internal static bool Contains(string x, string y)
        {
            return (x.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0);
        }
        
        internal static string ToString(TransferEncoding encoding)
        {
            switch (encoding)
            {
                default:
                    throw new NotSupportedException();

                case TransferEncoding.Base64:
                    return Protocol.TransferEncodingBase64;

                case TransferEncoding.SevenBit:
                    return Protocol.TransferEncoding7Bit;

                case TransferEncoding.QuotedPrintable:
                    return Protocol.TransferEncodingQuoted;
            }
        }
        
        internal static string ToString(DigestAlgorithm algorithm)
        {
            switch(algorithm)
            {
                default:
                    throw new NotSupportedException();                
                
                case DigestAlgorithm.SHA1:
                    return "SHA1";
            }
        }
    }
}
